
#ifndef __M_ROOM_H__
#define __M_ROOM_H__

#include "db.hpp"
#include "util.hpp"
#include "onlineuser.hpp"
#include <vector>
#include <memory>
enum class room_status
{
    GAMING,
    GAMEOVER
};
#define CHESS_ROW 15
#define CHESS_COL 15

#define CHESS_WHITE 1 // 白棋下棋的数值
#define CHESS_BLACK 2 // 黑棋下棋的数值

// 游戏房间设计与管理
// 玩家的请求(聊天/下棋/退出房间)都会交给对应的房间处理
// 房间根据玩家不同的请求，生成响应，并将响应广播给其他玩家
class room
{
public:
    using ptr = std::shared_ptr<room>;
    room(uint64_t room_id, user_table *usertb, online_user *online)
        : _room_id(room_id), _stat(room_status::GAMING), _player_count(0),
          _usertb(usertb), _online(online),
          _board(CHESS_ROW, std::vector<int>(CHESS_COL, 0))
    {
        DLOG("房间创建成功:%lu!!!", _room_id);
    }
    ~room()
    {
        DLOG("房间销毁成功:%lu!!!", _room_id);
    }
    // 添加一个白棋用户
    void add_white_user(uint64_t uid)
    {
        _white_id = uid;
        _player_count++;
    }
    // 添加一个黑棋用户
    void add_black_user(uint64_t uid)
    {
        _black_id = uid;
        _player_count++;
    }
    // 获取白棋用户id
    uint64_t get_white_user()
    {
        return _white_id;
    }
    // 获取黑棋id
    uint64_t get_black_user()
    {
        return _black_id;
    }
    // 获取房间号
    uint64_t get_roomid()
    {
        return _room_id;
    }
    // 获取房间状态
    room_status get_status()
    {
        return _stat;
    }
    // 获取房间玩家的个数
    int get_player_count()
    {
        return _player_count;
    }

    // 处理下棋的请求
    // 请求格式为:{"optype":"put_chess","room_id":xxx,"uid":xxx,"row":x,"col":y}
    // put_chess表示下棋请求，room_id表示房间号,uid表示下棋动作的玩家id,row和col表示下棋的位置
    Json::Value handle_chess(Json::Value &req)
    {
        Json::Value resp = req;
        // // 1,判断该请求中的房间号是否和当前的房间号相同
        // uint64_t room_id = req["room_id"].asUInt64();
        // if (room_id != _room_id)
        // {
        //     resp["optype"] = "put_chess";
        //     resp["result"] = false;
        //     resp["reason"] = "房间号不匹配";
        //     return resp;
        // }
        // 2,判断黑棋和白棋两者是否有一个下线了
        int chess_row = req["row"].asInt();
        int chess_col = req["col"].asInt();
        uint64_t cur_id = req["uid"].asUInt64(); // 执行下棋动作的用户id
        if (_online->is_in_game_room(_white_id) == false)
        {
            resp["result"] = true;
            resp["reason"] = "对方掉线了，恭喜您获胜!!!";
            resp["winner"] = _black_id;
        }
        else if (_online->is_in_game_room(_black_id) == false)
        {
            resp["result"] = true;
            resp["reason"] = "对方掉线了，恭喜您获胜!!!";
            resp["winner"] = _white_id;
        }
        // 3,获取下棋位置判断是否合法
        if (_board[chess_row][chess_col] != 0)
        {
            resp["result"] = false;
            resp["reason"] = "该位置无法落子!!!";
            return resp;
        }
        int cur_color = cur_id == _white_id ? CHESS_WHITE : CHESS_BLACK;
        _board[chess_row][chess_col] = cur_color;
        // 4，判断是否胜利
        uint64_t winerid = check_win(chess_row, chess_col, cur_color);
        // winerid=0表示没有用户胜利
        if (winerid != 0)
        {
            resp["reason"] = "获胜，五星连珠!!!";
        }
        resp["result"] = true;
        resp["winner"] = winerid;
        return resp;
        // 发送的响应是info,包含用户的id
    }

    // 处理发送消息的请求
    // req["message"]包含要发送的消息
    Json::Value handle_chat(Json::Value &req)
    {
        Json::Value resp = req;
        // // 1,判断房间号是否一致
        // if (req["room_id"].asUInt64() != _room_id)
        // {
        //     resp["result"] = false;
        //     resp["reason"] = "房间号不一致";
        //     return resp;
        // }
        // 2,将消息广播给房间内的其他用户
        resp["result"] = true;

        return resp;
    }
    // 退出房间的请求
    void handle_exit(uint64_t uid)
    {
        // 如果是下棋中退出，则对方获胜，否则正常退出
        Json::Value resp;
        if (_stat == room_status::GAMING)
        {
            uint64_t winerid=uid == _white_id ? _black_id : _white_id;
            resp["optype"] = "put_chess";
            resp["result"] = true;
            resp["reason"] = "对方掉线了，恭喜您获胜!!!";
            resp["room_id"] = _room_id;
            resp["uid"] = uid;
            resp["row"] = -1;
            resp["col"] = -1;
            resp["winner"] =winerid;
            uint64_t loser_id = winerid == _white_id ? _black_id : _white_id;
            _usertb->win(winerid);
            _usertb->lose(loser_id);
            _stat = room_status::GAMEOVER;
            broadcast(resp);
        }
        _player_count--;
    }
    // 处理请求总的方法，根据请求类型，选择不同的处理方法，在构建响应，广播给其他玩家
    void handle_req(Json::Value &req)
    {
        // 判断房间号是否一致
        Json::Value resp;
        // 1,判断房间号是否一致
        if (req["room_id"].asUInt64() != _room_id)
        {
            resp["optype"] = req["optype"].asString();
            resp["result"] = false;
            resp["reason"] = "房间号不一致";
            return broadcast(resp);
        }

        // 2,根据不同请求类型调用不同的处理函数
        if (req["optype"].asString() == "put_chess")
        {
            resp = handle_chess(req);
            if (resp["winner"].asInt64() != 0)
            {
                uint64_t winerid = resp["winner"].asInt64();
                // 更新数据库信息
                uint64_t loser_id = winerid == _white_id ? _black_id : _white_id;
                _usertb->win(winerid);
                _usertb->lose(loser_id);
                _stat = room_status::GAMEOVER;
            }
        }
        else if (req["optype"].asString() == "chat")
        {
            resp = handle_chat(req);
        }
        else
        {
            resp["optype"] = req["optype"].asString();
            resp["result"] = false;
            resp["reason"] = "未知请求类型!!!";
            return broadcast(resp);
        }
        broadcast(resp);
    }
    // 将响应广播给房间内所有玩家
    void broadcast(Json::Value &resp)
    {
        // 1,将响应序列化后发送
        std::string body;
        Json_util::seralize(resp, body);
        // 2,发送给房间内所有玩家
        wsserver_t::connection_ptr conn1 = _online->get_conn_from_room(_white_id);
        if (conn1.get() != nullptr&&conn1->get_state() == websocketpp::session::state::open)
        {
            conn1->send(body);
        }
        wsserver_t::connection_ptr conn2 = _online->get_conn_from_room(_black_id);
        if (conn2.get() != nullptr&&conn2->get_state() == websocketpp::session::state::open)
        {
            conn2->send(body);
        }
    }

private:
    // 判断是否五星连珠
    bool five(int row, int col, int row_off, int col_off, int color)
    {
        int count = 1;
        int search_row = row + row_off;
        int search_col = col + col_off;
        while (search_row >= 0 && search_row < CHESS_ROW &&
               search_col >= 0 && search_col < CHESS_COL && _board[search_row][search_col] == color)
        {
            count++;
            search_row += row_off;
            search_col += col_off;
        }
        if (count >= 5)
        {
            return true;
        }

        search_row = row - row_off;
        search_col = col - col_off;
        while (search_row >= 0 && search_row < CHESS_ROW &&
               search_col >= 0 && search_col < CHESS_COL && _board[search_row][search_col] == color)
        {
            count++;
            search_row -= row_off;
            search_col -= col_off;
        }
        if (count >= 5)
        {
            return true;
        }
        return false;
    }
    // 判断是否胜利
    uint64_t check_win(int row, int col, int color)
    {
        if (five(row, col, 0, 1, color) || five(row, col, 1, 0, color) || five(row, col, -1, 1, color) || five(row, col, -1, -1, color))
        {
            if (color == CHESS_WHITE)
                return _white_id;
            else
                return _black_id;
        }
        return 0;
    }

private:
    uint64_t _room_id;                    // 游戏房间的id
    room_status _stat;                    // 房间的状态(游戏结束or游戏中)
    int _player_count;                    // 房间玩家的个数(个数为0时需要释放房间)
    uint64_t _white_id;                   // 白棋玩家的id
    uint64_t _black_id;                   // 黑棋玩家的id
    user_table *_usertb;                  // 用户信息的管理
    online_user *_online;                 // 在线用户管理(通过用户id获取用户对应的socket连接)
    std::vector<std::vector<int>> _board; // 棋盘
};

// 房间管理模块设计
// 对所有房间信息进行管理，并提供创建房间，查找房间，销毁房间等接口
class roomManager
{
public:
    roomManager(online_user *online, user_table *us_tb)
        : _online(online), _us_tb(us_tb), _next_rid(1)
    {
        DLOG("房间管理模块初始化成功!!!");
    }
    // 创建一个房间(两个用户用户id)
    room::ptr createRoom(uint64_t uid1, uint64_t uid2)
    {
        // 1,判断这两个用户是否在游戏大厅中
        if (_online->is_in_game_hall(uid1) == false)
        {
            DLOG("%ld不在游戏大厅中!!!", uid1);
            return room::ptr();
        }
        if (_online->is_in_game_hall(uid2) == false)
        {
            DLOG("%ld不在游戏大厅中!!!", uid2);
            return room::ptr();
        }
        std::unique_lock<std::mutex> lock(_mutex);
        // 2,创建一个新房间，并添加管理
        room::ptr rp(new room(_next_rid, _us_tb, _online));
        rp->add_black_user(uid1);
        rp->add_white_user(uid2);

        _rooms.insert(std::make_pair(_next_rid, rp));
        _users.insert(std::make_pair(uid1, _next_rid));
        _users.insert(std::make_pair(uid2, _next_rid));
        _next_rid++;
        return rp;
    }
    // 移除一个房间
    void remove_Room(uint64_t rid)
    {
        room::ptr rp = get_room_by_rid(rid);
        if (rp.get() == nullptr)
        {
            DLOG("移除%ld房间失败，房间信息不存在!!!", rid);
            return;
        }
        uint64_t uid1 = rp->get_black_user();
        uint64_t uid2 = rp->get_white_user();

        std::unique_lock<std::mutex> lock(_mutex);
        _users.erase(uid1);
        _users.erase(uid2);
        // 2,移除房间管理信息
        _rooms.erase(rid);
    }
    // 删除指定用户
    void remove_Room_user(uint64_t uid)
    {
        room::ptr rp = get_room_by_uid(uid);
        if (rp.get() == nullptr)
        {
            return;
        }
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _users.erase(uid);
        }
        // 从该房间中移除用户
        rp->handle_exit(uid);
        // 如果房间中用户数为0，删除房间信息
        if (rp->get_player_count() == 0)
        {
            remove_Room(rp->get_roomid());
        }
    }
    // 查找房间信息
    // 通过房间id获取房间信息
    room::ptr get_room_by_rid(uint64_t id)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _rooms.find(id);
        if (it == _rooms.end())
        {
            return room::ptr();
        }
        return it->second;
    }
    // 通过用户id获取房间信息
    room::ptr get_room_by_uid(uint64_t id)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _users.find(id);
        if (it == _users.end())
        {
            return room::ptr();
        }
        auto rit = _rooms.find(it->second);
        if (rit == _rooms.end())
        {
            return room::ptr();
        }
        return rit->second;
    }

private:
    std::mutex _mutex;
    online_user *_online;                           // 在线用户管理句柄
    user_table *_us_tb;                             // 数据管理句柄
    uint64_t _next_rid;                             // 房间id，使用一个计数器来实现
    std::unordered_map<uint64_t, room::ptr> _rooms; // 房间id和房间信息的映射关系
    std::unordered_map<uint64_t, uint64_t> _users;  // 用户id和房间id的映射关系
};

#endif